package com.xmh.autocode.main;

import com.alibaba.druid.pool.DruidDataSource;
import com.xmh.myutil.util.FileUtil;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import javax.annotation.Resource;
import java.io.*;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 代码自动生成 --- spring boot jpa
 *
 * @author xmh
 */
//@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("/spring_bean.xml")
//@Transactional
@SpringBootApplication
public class AutomaticCodeGenerationByJPA {

    private final String vo_Path = "F:\\eclipseWord2\\file\\dandy\\dandy-database\\src\\main\\java\\com\\zlhy\\dandy\\database\\entity\\";

    private final String dao_Path = "F:\\eclipseWord2\\file\\dandy\\dandy-database\\src\\main\\java\\com\\zlhy\\dandy\\database\\jpa\\";

    private final String controller_Path = "F:\\eclipseWord2\\file\\dandy\\dandy-business\\src\\main\\java\\com\\zlhy\\dandy\\business\\controller\\";

    private final String service_Path = "F:\\eclipseWord2\\file\\dandy\\dandy-business\\src\\main\\java\\com\\zlhy\\dandy\\business\\service\\";

    private final String[] table_Name_List = {"t_withdraw"};

    @Resource(name = "dataSource")
    private DruidDataSource comboPooledDataSource;

    private final String controle_Code = FileUtil
            .getFileStr(this.getClass().getResource("/model/JPA/controleModel.model").getPath());

    private final String service_Impl_Code = FileUtil
            .getFileStr(this.getClass().getResource("/model/JPA/serviceImplementsModel.model").getPath());

    private final String service_Inte_Code = FileUtil
            .getFileStr(this.getClass().getResource("/model/JPA/serviceInterfaceModel.model").getPath());

    private final String dao_Inte_Code = FileUtil
            .getFileStr(this.getClass().getResource("/model/JPA/daoInterface.model").getPath());

    @Test
    public void create() throws Exception {
        List<Object> tableList = selectTalbeDetailed(false, table_Name_List);

        createVo(tableList);
        createMode(tableList, dao_Inte_Code, dao_Path, "Repository");
        createMode(tableList, controle_Code, controller_Path, "Controller");
        createMode(tableList, service_Inte_Code, service_Path, "Service");
        createMode(tableList, service_Impl_Code, service_Path, "ServiceImpl");
    }

    /**
     * 根据模板文件创建对应的文件以及对应路径
     *
     * @param tableList
     * @param modeCode
     * @param path
     * @param nameSuffix
     */
    @SuppressWarnings("unchecked")
    private void createMode(List<Object> tableList, String modeCode, String path, String nameSuffix) {
        for (Object object : tableList) {
            Map<String, Object> tableMap = (Map<String, Object>) object;
            // 转为驼峰
            String voName = nameTransformation((String) tableMap.get("tableName"));
            // 首字母大写
            String className = titleCase(voName);

            String filePath = path;
            String fileName = className + nameSuffix;

            filePath += fileName.substring(fileName.length() - 4).equals("Impl") ? "\\impl\\" : "";

            String code = extracted(voName, className, modeCode);

            outFile(code, filePath, fileName, "java");
        }
    }

    /**
     * 替换对应的包名-类型，以及表名
     *
     * @param voName
     * @param className
     * @param code
     * @return
     */
    private String extracted(String voName, String className, String code) {
        String nowStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());

        String replace = code.replace("__LowercaseTtableName__", voName);
        replace = replace.replace("__UpperCaseTtableName__", className);
        replace = replace.replace("____Date____", nowStr);
        replace = replace.replace("___EPath___", getPackagePath(vo_Path));
        replace = replace.replace("__Service__", getPackagePath(service_Path));
        replace = replace.replace("__DAO__", getPackagePath(dao_Path));
        replace = replace.replace("__Controller__", getPackagePath(controller_Path));

        return replace;
    }

    /**
     * 根据文件相对路径获取包路径
     *
     * @param path
     * @return
     */
    private String getPackagePath(String path) {
        String str = path.split("src")[1];
        String substring = str.substring(1, str.length() - 1);

        if (!substring.startsWith("com")) {
            String string = path.split("java")[1];
            substring = string.substring(1, string.length() - 1);
        }

        String packPach = substring.replace("\\", ".");
        return packPach;
    }

    /**
     * 创建实体类
     *
     * @param tableNameMaplist
     */
    @SuppressWarnings("unchecked")
    private void createVo(List<Object> tableNameMaplist) {
        for (Object object : tableNameMaplist) {
            Map<String, Object> tableMap = (Map<String, Object>) object;

            String tableName = (String) tableMap.get("tableName");
            // 转为驼峰
            String voName = nameTransformation(tableName);
            // 首字母大写
            String className = titleCase(voName) + "Entity";

            StringBuffer voClassFileSb = new StringBuffer();

            voClassFileSb.append("package " + getPackagePath(vo_Path) + ";\n\n");
            voClassFileSb.append("import java.util.Date;\n");
            voClassFileSb.append("import java.math.BigDecimal;\n");
            voClassFileSb.append("import java.sql.Timestamp;\n");
            voClassFileSb.append("import javax.persistence.Entity;\n");
            voClassFileSb.append("import javax.persistence.GeneratedValue;\n");
            voClassFileSb.append("import javax.persistence.GenerationType;\n");
            voClassFileSb.append("import lombok.Data;\n");
            voClassFileSb.append("import javax.persistence.Id;\n");
            voClassFileSb.append("import javax.persistence.Table;\n");
            voClassFileSb.append("import java.io.Serializable;\n\n");

            voClassFileSb.append("import javax.persistence.Temporal;\n");
            voClassFileSb.append("import javax.persistence.TemporalType;\n");
            voClassFileSb.append("import org.hibernate.annotations.CreationTimestamp;\n");
            voClassFileSb.append("import org.hibernate.annotations.UpdateTimestamp;\n\n");

            voClassFileSb.append("@Data\n");
            voClassFileSb.append("@Entity\n");
            voClassFileSb.append("@Table(name = \"" + tableName + "\")\n");
            voClassFileSb.append("public class " + className + " implements Serializable {\n\n");

            long nextLong = new Random(System.currentTimeMillis()).nextLong();

            voClassFileSb.append("	/**\n");
            voClassFileSb.append("	 * 版本号\n");
            voClassFileSb.append("	 */\n");
            voClassFileSb.append("	private static final long serialVersionUID = " + nextLong + "L;\n\n");

            List<Object> list = (List<Object>) tableMap.get("columnList");
            for (int i = 0; i < list.size(); i++) {
                Map<String, Object> columnMap = (Map<String, Object>) list.get(i);

                String columnName = (String) columnMap.get("columnName");
                // 字段名转为驼峰
                String cName = nameTransformation(columnName);

                String remarks = (String) columnMap.get("remarks");

                // 转为对应的java类型
                String javaType = typeTransformation((String) columnMap.get("type"));

                voClassFileSb.append("	/**\n	 *" + remarks + "\n	 */\n");

                if (i == 0) {
                    voClassFileSb.append("	@Id\n");
                    voClassFileSb.append("	@GeneratedValue(strategy = GenerationType.IDENTITY)\n");
                }
                if ("updateDate".equals(cName)) {
                    voClassFileSb.append("    @Temporal(TemporalType.TIMESTAMP)\n");
                    voClassFileSb.append("    @UpdateTimestamp\n");
                }
                if ("createDate".equals(cName)) {
                    voClassFileSb.append("    @Temporal(TemporalType.TIMESTAMP)\n");
                    voClassFileSb.append("    @CreationTimestamp\n");
                }

                voClassFileSb.append("	private " + javaType + " " + cName + ";\n\n");
            }

            voClassFileSb.append("\n}");

            outFile(voClassFileSb.toString(), vo_Path, className, "java");
        }
    }

    private String typeTransformation(String type) {
        if (type.equals("INT") || type.equals("TINYINT") || type.equals("SMALLINT")) {
            return "Integer";
        } else if (type.equals("VARCHAR") || type.equals("TEXT")) {
            return "String";
        } else if (type.equals("BIGINT") || type.equals("BIGINT UNSIGNED")) {
            return "Long";
        } else if (type.equals("BIT")) {
            return "Boolean";
        } else if (type.equals("FLOAT")) {
            return "Float";
        } else if (type.equals("DOUBLE")) {
            return "Double";
        } else if (type.equals("TIMESTAMP")) {
            return "Timestamp";
        } else if (type.equals("DECIMAL") || type.equals("DECIMAL UNSIGNED")) {
            return "BigDecimal";
        } else if (type.equals("DATETIME") || type.equals("DATE")) {
            return "Date";
        }
        return type;
    }

    /**
     * 根据表名获取到表格的详细数据
     *
     * @param tableNames
     * @return
     * @throws SQLException
     */
    private List<Object> selectTalbeDetailed(boolean isAll, String... tableNames) throws SQLException {
        List<Object> tableNameMaplist = new ArrayList<Object>();

        // 获得元数据
        DatabaseMetaData metaData = comboPooledDataSource.getConnection().getMetaData();

        List<String> tableNameList = new ArrayList<String>(Arrays.asList(tableNames));

        if (isAll) {
            tableNameList.clear();
            ResultSet tables = metaData.getTables(null, null, null, new String[]{"TABLE"});
            while (tables.next()) {
                tableNameList.add(tables.getString("TABLE_NAME"));
            }
        }

        for (String talbeName : tableNameList) {
            // 通过表名获得所有字段名
            ResultSet columns = metaData.getColumns(null, null, talbeName, "%");

            List<Object> list = new ArrayList<Object>();

            // 获得所有字段名
            while (columns.next()) {
                Map<String, String> columnNameMap = new HashMap<>(); // 保存字段名

                columnNameMap.put("type", columns.getString("TYPE_NAME"));
                columnNameMap.put("columnName", columns.getString("COLUMN_NAME"));
                columnNameMap.put("remarks", columns.getString("REMARKS"));

                list.add(columnNameMap);
            }
            Map<String, Object> tableNameMap = new HashMap<>();
            tableNameMap.put("tableName", talbeName);
            tableNameMap.put("columnList", list);
            tableNameMaplist.add(tableNameMap);
        }

        return tableNameMaplist;
    }

    /**
     * 输出文件到指定目录文件夹
     *
     * @param code
     * @param path
     * @param fileName
     * @param fileType
     */
    private void outFile(String code, String path, String fileName, String fileType) {
        try {
            File file = new File(path + "//" + fileName + "." + fileType);

            if (!file.exists()) {
                new File(path).mkdirs();
                file.createNewFile();
            }
            OutputStreamWriter write = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");
            BufferedWriter writer = new BufferedWriter(write);
            writer.write(code);
            writer.flush();
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private String nameTransformation(String name) {
        if (name.indexOf("T_") == 0 || name.indexOf("t_") == 0) {
            name = name.substring(2, name.length());
        }

        Pattern linePattern = Pattern.compile("_(\\w)");
        name = name.toLowerCase();
        Matcher matcher = linePattern.matcher(name);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        String string = sb.toString();

        return string;

    }

    /**
     * 首字母大写
     *
     * @param string
     * @return
     */
    private String titleCase(String string) {
        return string.substring(0, 1).toUpperCase() + string.substring(1, string.length());
    }

}
